Web app 是圖像化的操作介面,使用者透過 UI 介面執行任務,產生各種事件(event)。在頁面建置階段的 JavaScript 程式碼,還需要註冊事件處置器(event-handler),也就是告訴瀏覽器在這些事件發生時,要執行的函式。
在 web app 裡主要有這幾種事件類型:
使用者事件:點擊、鍵盤輸入、移動滑鼠
瀏覽器事件:網頁處於已載入或未載入的狀態
網路事件:來自伺服器的回應
計時器事件:setTimeout
或setInterval
瀏覽器的執行環境是單一執行緒模型(single-threaded execution model),也就是說 JavaScript 引擎一次只能處理一段程式碼。
想像一下你到福利熊超市買東西,結帳櫃台一次只能服務一位客人,當太多客人在排隊客人就會生氣,這個時候櫃台就會廣播「請支援收銀」,多開幾個櫃台分散排隊的人潮,加快結帳的速度。
不過這間 JavaScript 超市櫃台竟然只有一個(單執行緒)!所以每個客人(事件)都必須排在同一個隊伍裡,萬一前面的客人一次買了整個購物推車的東西,後面的客人就要等久一點,你一定有過這樣的經驗吧。
在瀏覽器的環境裡事件會一直發生,就像是超市一直會有人進來買東西,於是 JavaScript 也有一套管理事件的機制,事件依發生的順序被安排在一個隊列(event queue)裡,如果目前沒有程式碼在跑,位在 event queue 第一個的事件裡的程式碼會被提取出來執行,一直到 event queue 清空,這個機制叫做 event loop,因為它是個一直在檢查 event queue 的迴圈。這就很像我們進銀行要先抽號碼牌,叫到我們的號碼才到櫃台,在這之前只能等待。
我們可以事先寫一段函式在事件發生時讓瀏覽器執行,這是透過註冊 event-handlers,當符合條件的事件發生時,其函式就會被放進 event queue 當中。
Event-handlers 有二種,第一種是瀏覽器內建的 global event-handler,常見的有onload
, onclick
等等。
要注意內建的 event-handler 只能加載一個函式,如果註冊了新的函式,新函式會取代原有的函式。
var button = document.getElementById("button");
button.onclick = function(){
console.log("福利熊");
};
button.onclick = function(){
console.log("熊福利");
};
// 點擊 button 後只會印出 "熊福利"
如上範例我們取得頁面的 #button
元素,先在按鈕用內建的onclick
註冊會印出「福利熊」的函式,再註冊一個會印出「熊福利」的新函式。點擊按鈕卻只會得到「熊福利」的結果☹️,也就是說新函式覆蓋了之前註冊的事件函式。這在和別人共同開發專案時會帶來困擾,因為你的新函式會不小心覆蓋了同事的函式,讓原有的功能突然不能使用,麻煩的是這不會有錯誤訊息。
好在我們還有另外一個內建的 addEventListener
方法,可以在一個元素上註冊多個事件函式。
var button = document.getElementById("button");
button.addEventListener("click", function(){
console.log("福利熊");
});
button.addEventListener("click", function(){
console.log("熊福利");
});
// 點擊 button 後印出 "福利熊 熊福利"
在同一個按鈕上先後使用addEventListener
註冊二個函式在click
事件上,這一次點擊按鈕得到「福利熊 熊福利」的結果。